package fonctions;

import structure.Caracteristique;
import structure.Couche;
import structure.Matrice;
import ensemble.Echantillon;
import exceptions.OperationMatriceException;
import exceptions.OperationMatriceException;

public class Apprentissage {
	public Caracteristique c;
	public Echantillon ech; 
	public Matrice error;
	public Matrice verror;
	
	public Apprentissage(Caracteristique c) {
		this.c= c;
		ech= null;
	}
	
	public Couche[] apprentissage(Couche[] couches) {
		return propagation(couches);
	}
	
	public Couche[] propagation(Couche[] couches) {
		// Activation couche 1
		try {
			couches[0].somme= (couches[0].poids.produit(ech.getSequence())).somme(couches[0].biais);
			//couches[0].somme= (couches[0].poids.produit(ech.getSequence()));
			//System.out.println(couches[0].somme);
		}catch (OperationMatriceException pb) {
			System.out.println(pb+"4");
		}
		switch (couches[0].fonction) {
		case 1:
			couches[0].activat= couches[0].somme;
			break;
		case 2:
			couches[0].activat= Operation.logSigmoid(couches[0].somme);
			//System.out.println(couches[0].activat);
			break;
		default:
			System.out.println("Unknown fonction");
			System.exit(0);
			break;
		}
		
		// Matrice Jacobs
		for (int ligne=0; ligne<couches[0].jacobs.getLignes(); ligne++) {
			for (int colonne=0; colonne<couches[0].jacobs.getColonnes(); colonne++) {
				if (ligne==colonne) {
					switch(couches[0].fonction) {
					case 1:
						couches[0].jacobs.getMatrice()[ligne][colonne]= 1;
						break;
					case 2:
						couches[0].jacobs.getMatrice()[ligne][colonne]= (1 - couches[0].activat.getMatrice()[ligne][0]) * couches[0].activat.getMatrice()[ligne][0];
						break;
					default:
						System.out.println("Unknown fonction");
						System.exit(0);
					}
				}
				else {
					couches[0].jacobs.getMatrice()[ligne][colonne]= 0;
				}
			}
		}
		
		// Activation couche [2, M]
		if (couches.length>=2) {
			for (int i=1; i<couches.length; i++) {
				try{
					couches[i].somme= (couches[i].poids.produit(couches[i-1].activat)).somme(couches[i].biais);
					//couches[i].somme= (couches[i].poids.produit(couches[i-1].activat));
				}catch (OperationMatriceException ex) {
					System.out.println(ex+"5");
				}
				switch (couches[i].fonction) {
				case 1:
					couches[i].activat= couches[i].somme;
					//System.out.println(couches[i].activat);
					break;
				case 2:
					couches[i].activat= Operation.logSigmoid(couches[i].somme);
					break;
				default:
					System.out.println("Unknown fonction");
					System.exit(0);
					break;
				}
				
				// Matrice Jacobs
				for (int ligne=0; ligne<couches[i].jacobs.getLignes(); ligne++) {
					for (int colonne=0; colonne<couches[i].jacobs.getColonnes(); colonne++) {
						if (ligne==colonne) {
							switch(couches[i].fonction) {
							case 1:
								couches[i].jacobs.getMatrice()[ligne][colonne]= 1;
								break;
							case 2:
								couches[i].jacobs.getMatrice()[ligne][colonne]= (1 - couches[i].activat.getMatrice()[ligne][0]) * couches[i].activat.getMatrice()[ligne][0];
								break;
							default:
								System.out.println("Unknown fonction");
								System.exit(0);
							}
						}
						else {
							couches[i].jacobs.getMatrice()[ligne][colonne]= 0;
						}
					}
				}
			}
		}
		couches= retropropagation(couches);
		couches= majPoids(couches);
		couches= majBiais(couches);
		return couches;
	}
	
	public Couche[] retropropagation(Couche[] couches) {
		//error= Operation.sinus(couches[couches.length-1].activat, ech.getEtiquette());
		//System.out.println(error);
		
		Matrice eti = new Matrice (couches[couches.length-1].activat.getLignes(), 1);
		for (int i=0; i<eti.getLignes(); i++) {
			if (i==ech.getEtiquette()) {
				eti.getMatrice()[i][0]=0.9;
			}
			else {
				eti.getMatrice()[i][0]=0.1;
			}
		}
		
		error= Operation.taux(couches[couches.length-1].activat, ech.getEtiquette());
		try {
			verror = eti.soustraction(couches[couches.length-1].activat);
		}
		catch (Exception exception) {
			System.out.println(exception+"aqa");
		}
		//Matrice constante= new Matrice(1, 1);
		//constante.getMatrice()[0][0]= -2;
		
		double constante = -2;
		double erreur=error.getMatrice()[0][0];
		try {
			//couches[couches.length-1].sensit= constante.produit(couches[couches.length-1].jacobs.produit(error));
			//System.out.println(couches[couches.length-1].sensit.getLignes()+" | "+couches[couches.length-1].sensit.getColonnes());
			//System.out.println(couches[couches.length-1].sensit);
			//couches[couches.length-1].sensit= ((couches[couches.length-1].jacobs).produit(constante).produit(Operation.tauxVecteur1(couches[couches.length-1].activat, ech.getEtiquette())));
			//System.out.println(couches[couches.length-1].sensit.getLignes()+" | "+couches[couches.length-1].sensit.getColonnes());
			//System.out.println(couches[couches.length-1].sensit);
			//System.out.println(couches[couches.length-1].sensit);
			//System.out.println(error);
			//couches[couches.length-1].sensit=(couches[couches.length-1].jacobs.produit(error)).produit(constante);
			//System.out.println(couches[couches.length-1].sensit);
			couches[couches.length-1].sensit= Matrice.produit(couches[couches.length-1].jacobs, verror).produit(constante);
			//System.out.println(couches[couches.length-1].sensit);
		}catch (OperationMatriceException exc) {
			System.out.println(exc+"6");
		}
		if (couches.length>=2) {
			for (int j=couches.length-2; j>=0; j--) {
				try {
					//couches[j].sensit= couches[j].jacobs.produit(couches[j+1].poids.transpose().produit(couches[j+1].sensit));
					couches[j].sensit= Matrice.produit(Matrice.produit(couches[j].jacobs, couches[j+1].poids.transpose()), couches[j+1].sensit);
				}catch (OperationMatriceException pb) {
					System.out.println(pb+"3");
				}
			}
		}
		return couches;
	}
	
	private Couche[] majPoids(Couche[] couches) {
		try {
			//couches[0].poids= couches[0].poids.soustraction(couches[0].sensit.produit(ech.getSequence().transpose()));
			//System.out.println((ech.getSequence().transpose()));
			//System.out.println(couches[0].sensit);
			//System.out.println( Matrice.produit (couches[0].sensit , (ech.getSequence().transpose())));
			couches[0].poids= couches[0].poids.soustraction(   Matrice.produit(Matrice.produit (couches[0].sensit , (ech.getSequence().transpose())), c.alpha));
		}catch (OperationMatriceException e) {
			System.out.println(e+"1");
		}
		for (int i=1; i<couches.length; i++) {
			try {
			//couches[i].poids= couches[i].poids.soustraction((couches[i].sensit.produit(c.alpha)).produit(couches[i-1].activat.transpose()));
				couches[i].poids= couches[i].poids.soustraction(   Matrice.produit(Matrice.produit (couches[i].sensit , (couches[i-1].activat.transpose())), c.alpha));
			}catch (OperationMatriceException excep) {
				System.out.println(excep+"2");
			}
		}
		return couches;
	}
	
	private Couche[] majBiais(Couche[] couches) {
		for (int i=0; i<couches.length; i++) {
			try {
			couches[i].biais= couches[i].biais.soustraction( Matrice.produit (couches[i].sensit, c.alpha));
			}catch (OperationMatriceException excep) {
				System.out.println(excep+ "dfv");
			}
		}
		return couches;
	}
}
